package deepr.lib;

import deepr.core.DisposeContext;
import deepr.core.PropertyData;
import deepr.core.SchemaData;
import deepr.core.SchemaManager;
import deepr.exception.DeeprException;
import deepr.filter.FilterParser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;

@Slf4j
public class FilterLib {


  // [{"category":xxx,"levelA":xxx,"levelB":xxx},{"category":xxx,"levelA":xxx,"levelB":xxx}]
  // {"category":x,"levelA":xx} -> "category":x,"levelA":xx
  public static StringBuilder joinJsonKeyValue(String property, Object value) {
    Assert.isTrue(value instanceof HashMap, property + " value must be hashMap");
    HashMap<String, Object> v = (HashMap<String, Object>) value;
    StringBuilder sb = new StringBuilder();
    for (Map.Entry<String, Object> entry : v.entrySet()) {
      appendJoiner(sb, ",");
      sb.append(entry.getKey()).append(":").append(entry.getValue());
    }
    return sb;
  }

  public static void appendJoiner(StringBuilder sb, String joiner) {
    if (sb.length() > 0)
      sb.append(joiner);
  }

  public static String jsonKeyToInt(String key) {
    return "(" + key + ")::int8";
  }


  public static String[] concatJsonKey(String[] keys) {
    StringBuilder str = new StringBuilder();
    StringBuilder prefixKey = new StringBuilder();
    int len = keys.length;
    for (int i = 0; i < len; i++) {
      if (i == len - 1) {
        str.append("->>'").append(keys[i]).append("'");
        prefixKey.append("_").append(keys[i]);
      } else if (i > 0) {
        str.append("->").append(keys[i]);
        prefixKey.append("_").append(keys[i]);
      } else {
        str.append(keys[i]);
        prefixKey.append(keys[i]);
      }
    }
    return new String[]{str.toString(), prefixKey.toString()};
  }

  public static String parseSelectKeys(DisposeContext ctx, HashMap<String, PropertyData> fields, String object, String[] select) throws Exception {
    StringBuilder sb = new StringBuilder();
    for (String s : select) {
      if (sb.length() > 0)
        sb.append(",");
      PropertyData propertyData = fields.get(s);
      if (propertyData == null) {
        throw new DeeprException(s + " for " + object + " not exists");
      }
      s = propertyData.getName();
//      if (propertyData != null) {
      Class<?> handler = propertyData.getHandler();
      if (handler != null) {
        sb.append(s).append(":").append(
            handler.getMethod("select",
                DisposeContext.class, String.class, String.class, Object.class).
                invoke(handler.newInstance(), ctx, object, s, propertyData.getHandlerArgs()));
      } else
        sb.append(s).append(":").append(object).append(".").append(s);
//      }
//      else {
//        sb.append(s).append(":").append(object).append(".").append(s);
//      }
    }
    return sb.toString();
  }


  public static SchemaData getSchemaData(FilterParser filterParser, String parent, String object) throws DeeprException {
    if (parent == null) {
      SchemaData schemaData = filterParser.getCtx().getSchemaData();
      filterParser.setParentSchemaData(schemaData);
      return schemaData;
    }
    SchemaData parentSchemaData = filterParser.getParentSchemaData();
    log.debug("parent: {}, obj: {}, parentSchema: {}", parent, object, parentSchemaData);
    PropertyData propertyData = parentSchemaData.getPropertyData(object);
    if (propertyData != null && !propertyData.isSchema()) {
      throw new DeeprException(parent + " for " + object + " not is schema");
    }
    if (propertyData.isRelationProperty()) {
      SchemaData schemaData = parentSchemaData.getRelationSchemaData(object);
      if (schemaData == null) {
        throw new DeeprException(parent + " for " + object + " not is schema");
      }
      return schemaData;
    }
    return SchemaManager.getSchemaData(propertyData.getSchema());
  }


  public static SchemaData getSchemaData(DisposeContext ctx, String parent, String object) throws DeeprException {
    if (parent == null) {
      return ctx.getSchemaData();
    }
    SchemaData parentSchemaData = SchemaManager.getSchemaData(parent);
    log.debug("parent: {}, obj: {}, parentSchema: {}", parent, object, parentSchemaData);
    PropertyData propertyData = parentSchemaData.getPropertyData(object);
    if (propertyData == null || !propertyData.isSchema()) {
      throw new DeeprException(object + " for " + parent + " not is schema");
    }
    if (propertyData.isRelationProperty()) {
      SchemaData schemaData = parentSchemaData.getRelationSchemaData(object);
      if (schemaData == null) {
        throw new DeeprException(parent + " for " + object + " not is schema");
      }
      return schemaData;
    }
    return SchemaManager.getSchemaData(propertyData.getSchema());
  }

  public static String convertKey(FilterParser filterParser, String key) throws DeeprException {
    String convertAction = filterParser.getConvertAction();
    if (convertAction == null)
      return key;
    Map<String, String> convertMap = new HashMap<>();
    convertMap.put("unit", "%10");
    convertMap.put("decade", "/10%10");
    convertMap.put("hundred", "/100%10");
    convertMap.put("k", "/1000%10");
    convertMap.put("th", "/10000%10");
    String s = convertMap.get(convertAction);
    if (s == null) {
      if (convertAction.equals("to_date")) {
        switch (filterParser.getSchemaType()) {
          case NEO4J:
            return "substring(" + key + ", 0, 10)";
          case PG:
            return "to_date(" + key + ", 'yyyy-mm-dd')";
        }
      }
      throw new DeeprException(key + " for convert" + convertAction + " not Support");
    }
    return key + s;
  }

  public static void appendFilterStr(StringBuilder sb, StringBuilder filter) {
    if (filter.length() == 0) {
      return;
    }
    sb.append(" WHERE ").append(filter).append("\n");
  }


}
